home *** CD-ROM | disk | FTP | other *** search
/ Workbench Design / WB Collection.iso / workbench werkzeuge / memory & system tools / priman / source / util.c < prev    next >
C/C++ Source or Header  |  1996-04-07  |  30KB  |  1,113 lines

  1. /*
  2.  *        Task Priority Manager
  3.  *        Copyright 1993, 1994 Barry McConnell
  4.  *        bmccnnll@tcd.ie
  5.  *
  6.  *        A library of little functions called from within the main program,
  7.  *        sorted alphabetically.
  8.  *
  9.  *        Set tab stops to 4 when editing this file.
  10.  */
  11.  
  12. #include "PriMan.h"
  13.  
  14. /*
  15.  *        My version of sprintf requires this define, for the function passed to
  16.  *        RawDoFmt(). Thanks to Eddy Carroll & SnoopDos for this...
  17.  */
  18. #define RAWDOFMT_COPY (void (*))"\x16\xc0\x4e\x75"  /* MOVE.B D0,(A3)+ ; RTS */
  19.  
  20. /*
  21.  *        The BusyPointer() and NormalPointer() functions rely on these requester
  22.  *        structures. By declaring them as static, they aren't visible outside
  23.  *        this file and the calling functions need not worry about them.
  24.  */
  25. static struct Requester    req1, req2;
  26.  
  27. /*
  28.  *        It is possible that BusyPointer() might be called twice (or more) in
  29.  *        succession, by some code that doesn't realise one is already up. So we
  30.  *        use a variable (again only visible to this file) to say how many times
  31.  *        it has been called. Only the first time round do we actually put up the
  32.  *        busy pointer (the system will crash if we try to re-use the blocking
  33.  *        requester structure).
  34.  *
  35.  *        When NormalPointer() is called, the variable is decremented. Only once
  36.  *        it gets back down to zero do we restore the pointer.
  37.  */
  38. static int busyCount;
  39.  
  40. /*
  41.  *        This is an image of the standard Busy pointer, which the BusyPointer()
  42.  *        function uses when not running under V39.
  43.  */
  44. UWORD __chip waitPointer[] =
  45.     {
  46.     0x0000, 0x0000,
  47.     0x0400, 0x07c0,
  48.     0x0000, 0x07c0,
  49.     0x0100, 0x0380,
  50.     0x0000, 0x07e0,
  51.     0x07c0, 0x1ff8,
  52.     0x1ff0, 0x3fec,
  53.     0x3ff8, 0x7fde,
  54.     0x3ff8, 0x7fbe,
  55.     0x7ffc, 0xff7f,
  56.     0x7efc, 0xffff,
  57.     0x7ffc, 0xffff,
  58.     0x3ff8, 0x7ffe,
  59.     0x3ff8, 0x7ffe,
  60.     0x1ff0, 0x3ffc,
  61.     0x07c0, 0x1ff8,
  62.     0x0000, 0x07e0,
  63.     0x0000, 0x0000,
  64.     };
  65.  
  66. /*
  67.  *        Display a busy pointer in both windows, and prevent the user from
  68.  *        clicking on any gadgets. Also effectively disable the size gadget.
  69.  *        We are careful to make sure that each window is actually open before
  70.  *        putting up a busy pointer in it, and also that there isn't already a
  71.  *        busy pointer showing!
  72.  */
  73. void BusyPointer()
  74.     {
  75.     if (++busyCount == 1)
  76.         {
  77.         if (mainWindow)
  78.             {
  79.             /*
  80.              *        To disable the size gadget, we make the minimum and maximum window
  81.              *        dimensions equal to the current window dimensions.
  82.              */
  83.             WindowLimits(mainWindow,    mainWindow -> Width, mainWindow -> Height,
  84.                                         mainWindow -> Width, mainWindow -> Height);
  85.         
  86.             /*
  87.              *        To disable the main window's gadgets, we put up an invisible
  88.              *        "blocking requester".
  89.              */
  90.             Request(&req1, mainWindow);
  91.         
  92.             /*
  93.              *        There are two different ways of putting up a busy pointer, depending
  94.              *        on which OS version we're running under.
  95.              */
  96.             if (osver < 39)
  97.                 SetPointer(mainWindow, waitPointer, 16, 16, -6, 0);
  98.             else
  99.                 SetWindowPointer(mainWindow,    WA_BusyPointer,        TRUE,
  100.                                                 WA_PointerDelay,    TRUE,
  101.                                                 TAG_END);
  102.             }
  103.     
  104.         /*
  105.          *        The exact same things are done for the Settings window (except we
  106.          *        don't need to worry about the size gadget!).
  107.          */
  108.         if (setWindow)
  109.             {
  110.             Request(&req2, setWindow);
  111.     
  112.             if (osver < 39)
  113.                 SetPointer(setWindow, waitPointer, 16, 16, -6, 0);
  114.             else
  115.                 SetWindowPointer(setWindow,    WA_BusyPointer,        TRUE,
  116.                                             WA_PointerDelay,    TRUE,
  117.                                             TAG_END);
  118.             }
  119.         }
  120.     }
  121.  
  122.  
  123. /*
  124.  *        Close the main window if it's open, freeing up its gadgets and menus if
  125.  *        necessary. Also remember its position and dimensions in case we later
  126.  *        want to re-open it.
  127.  */
  128. void CloseMainWindow()
  129.     {
  130.     if (mainWindow)
  131.         {
  132.         if (menuStrip)
  133.             {
  134.             ClearMenuStrip(mainWindow);
  135.             FreeMenus(menuStrip);
  136.             }
  137.         GetPos();
  138.         CloseWindowSafely(mainWindow);
  139.         mainWindow = NULL;
  140.         }
  141.  
  142.     FreeGadgets(mainGads);
  143.     mainGads = NULL;
  144.  
  145.     /*
  146.      *        Chances are we will also have opened some fonts. We close them here,
  147.      *        and then free up the VisualInfo structure, as well as releasing our
  148.      *        lock on the screen.
  149.      */
  150.     if (propFont)
  151.         {
  152.         CloseFont(propFont);
  153.         propFont = NULL;
  154.         }
  155.  
  156.     if (monoFont)
  157.         {
  158.         CloseFont(monoFont);
  159.         monoFont = NULL;
  160.         }
  161.  
  162.     if (visInfo)
  163.         {
  164.         FreeVisualInfo(visInfo);
  165.         visInfo = NULL;
  166.         }
  167.  
  168.     if (myScreen)
  169.         {
  170.         UnlockPubScreen(NULL, myScreen);
  171.         myScreen = NULL;
  172.         }
  173.     }
  174.  
  175.  
  176. /*
  177.  *        Close the settings window if it's open. It's tricky to free up any
  178.  *        attached gadgets, since the last gadget in the list of gadgets pointed
  179.  *        to by setGads[3] (the Page gadget, and the buttons at the bottom of the
  180.  *        window) may have been modified to point to one of the other gadget
  181.  *        lists, depending on what is showing. So we first remove the "other"
  182.  *        gadget list (using the page variable to see what it is) which should
  183.  *        sever the link, before freeing each individual list. It should never
  184.  *        happen that the Settings window is open without a second set of gadgets
  185.  *        in it, but even if this occurs, RemoveGList() won't mind if it doesn't
  186.  *        find them.
  187.  */
  188. void CloseSettingsWindow()
  189.     {
  190.     int i;  /* page number */
  191.  
  192.     if (setWindow)
  193.         {
  194.         RemoveGList(setWindow, setGads[page], -1);
  195.         CloseWindowSafely(setWindow);
  196.         setWindow = NULL;
  197.         }
  198.     for (i = 0; i < 4; i++)
  199.         {
  200.         FreeGadgets(setGads[i]);
  201.         setGads[i] = NULL;
  202.         }
  203.     }
  204.  
  205.  
  206. /*
  207.  *        Safely close a window that might have a shared IDCMP. This is done by
  208.  *        removing all messages from the window's MsgPort that refer to the
  209.  *        window. (We can't just remove every message, in case some of them are
  210.  *        actually meant for another window with the same IDCMP.)
  211.  */
  212. void CloseWindowSafely(struct Window *window)
  213.     {
  214.     struct IntuiMessage    *msg;    /* message in window's port    */
  215.     struct Node            *succ;    /* next message                */
  216.  
  217.     /*
  218.      *        First we need to disable multitasking, so we don't run into race
  219.      *        conditions with Intuition.
  220.      */
  221.     Forbid();
  222.  
  223.     /*
  224.      *        Next we get a pointer to the first message in the window's MsgPort.
  225.      */
  226.     msg = (struct IntuiMessage *)window -> UserPort -> mp_MsgList.lh_Head;
  227.  
  228.     /*
  229.      *        We loop around until we reach the last message.
  230.      */
  231.     while (succ = msg -> ExecMessage.mn_Node.ln_Succ)
  232.         {
  233.         if (msg -> IDCMPWindow == window)
  234.             {
  235.             /*
  236.              *        Once we have a message destined for this window, we remove
  237.              *        it from the MsgPort, and reply to it.
  238.              */
  239.             Remove((struct Node *)msg);
  240.             ReplyMsg((struct Message *)msg);
  241.             }
  242.  
  243.         /*
  244.           *        "msg" will be invalid if it was freed above, which is why we
  245.          *        took a copy of its successor at the start of the loop.
  246.          */
  247.         msg = (struct IntuiMessage *)succ;
  248.         }
  249.  
  250.     /*
  251.      *        When we close the window, we don't want Intuition to free up the
  252.      *        MsgPort itself (in case another window uses it too), so we remove
  253.      *        all traces of it here. We also set the window's IDCMP to NULL so
  254.      *        Intuition doesn't try sending it any more messages.
  255.      */
  256.     window -> UserPort = NULL;
  257.     ModifyIDCMP(window, NULL);
  258.  
  259.     /*
  260.      *        Finally we can re-enable multitasking, and close the window safely.
  261.      */
  262.     Permit();
  263.     CloseWindow(window);
  264.     }
  265.  
  266.  
  267. /*
  268.  *        Compare two strings. Like strcmp() but case-insensitive.
  269.  */
  270. int Compare(char *s, char *t)
  271.     {
  272.     /*
  273.      *        This bit is tricky. The loop only continues as long as:
  274.      *
  275.      *        - The upper-case versions of the current character in each string
  276.      *          match, and;
  277.      *
  278.      *        - The current character in the first string is not NULL (if we have
  279.      *          reached a NULL in the second string instead, the first condition
  280.      *          above would not hold true!);
  281.      *
  282.      *        (Okay, so I could have used K&R's slightly lengthier version...)
  283.      */
  284.     for (; ((*s & ~32) == (*t & ~32)) && *s; s++, t++)
  285.         ;  /* empty body */
  286.  
  287.     /*
  288.      *        To be true to the original strcmp() function, we return:
  289.      *
  290.      *        - Zero if the strings match (in this case, we will have reached the
  291.      *          end of both strings, and subtracting the NULL terminators gives
  292.      *          zero), or;
  293.      *
  294.      *        - A positive or negative number, indicating which string is
  295.      *          alphabetically greater.
  296.      */
  297.     return (*s & ~32) - (*t & ~32);
  298.     }
  299.  
  300.  
  301. /*
  302.  *        Stub for Compare() which is used when inserting tasks into the task
  303.  *        list. This skips over opening brackets (for frozen tasks) in either
  304.  *        string.
  305.  */
  306. int CompareTasks(char *s, char *t)
  307.     {
  308.     if (*s == '(')
  309.         s++;
  310.     if (*t == '(')
  311.         t++;
  312.     return Compare(s, t);
  313.     }
  314.  
  315.  
  316. /*
  317.  *        Tiny function to draw the bevel box in the Settings window.
  318.  */
  319. void DrawSettingsBox()
  320.     {
  321.     DrawBevelBox(setWindow -> RPort, INTERWIDTH, setGadStart - INTERHEIGHT,
  322.                     setWindow -> Width - INTERWIDTH * 2, setGadHeight + INTERHEIGHT * 2,
  323.                     GTBB_Recessed,    TRUE,
  324.                     GT_VisualInfo,    visInfo,
  325.                     TAG_END);
  326.     }
  327.  
  328.  
  329. /*
  330.  *        Join together a font name and its size. The characters in the font name up
  331.  *        to the ".font" part are used.
  332.  */
  333. void FontString(struct TextAttr *font, char *dest)
  334.     {
  335.     int i = 0;  /* index into font name */
  336.     
  337.     while ((font -> ta_Name)[i] != '.')
  338.         *(dest++) = (font -> ta_Name)[i++];
  339.     sprintf(dest, " %ld", font -> ta_YSize);
  340.     }
  341.  
  342.  
  343. /*
  344.  *        Check if a task has been frozen. Just looks at the State field and
  345.  *        compares against our two possible frozen states.
  346.  */
  347. BOOL Frozen(struct Task *task)
  348.     {
  349.     switch (task -> tc_State)
  350.         {
  351.         case FROZEN:
  352.         case FROZENREADY:
  353.             return TRUE;
  354.             break;
  355.  
  356.         default:
  357.             return FALSE;
  358.             break;
  359.         }
  360.     }
  361.  
  362.  
  363. /*
  364.  *        Remember the top entry in the ListView (used before regenerating it).
  365.  *        Under 2.x the best we can do is assume the top entry is the currently-
  366.  *        selected entry, but under 3.x we can find out exactly what it is.
  367.  */
  368. void GetListTop()
  369.     {
  370.     if (osver < 39)
  371.         {
  372.         if (current)
  373.             top = pos;
  374.         else
  375.             top = 0;
  376.         }
  377.     else
  378.         GT_GetGadgetAttrs(listGad, mainWindow, NULL,
  379.                             GTLV_Top,    &top,
  380.                             TAG_END);
  381.     }
  382.  
  383.  
  384. /*
  385.  *        Remember the co-ordinates of the main window (used before closing it).
  386.  */
  387. void GetPos()
  388.     {
  389.     if (mainWindow)
  390.         {
  391.         winLeft = mainWindow -> LeftEdge;
  392.         winTop = mainWindow -> TopEdge;
  393.         winWidth = mainWindow -> Width;
  394.         winHeight = mainWindow -> Height;
  395.         }
  396.     }
  397.  
  398.  
  399. /*
  400.  *        Fire up the AmigaGuide file - this is PriMan's online help feature. If
  401.  *        we have not already opened amigaguide.library, we should do so now, and
  402.  *        refuse to continue if it's not around.
  403.  */
  404. void Help()
  405.     {
  406.     static struct Screen *guideScreen;  /* screen we're open on now */
  407.  
  408.     struct EasyStruct noHelp =
  409.         {
  410.         sizeof(struct EasyStruct),
  411.         0,
  412.         "PriMan warning",
  413.         "Can't open amigaguide.library -\nno online help available.",
  414.         "Okay"
  415.         };
  416.  
  417.     if (!AmigaGuideBase)
  418.         if (!(AmigaGuideBase = OpenLibrary("amigaguide.library", 34)))
  419.             {
  420.             SimpleRequest(&noHelp, NULL);
  421.             return;
  422.             }
  423.  
  424.     /*
  425.      *        If this is the first time the user has asked for help, we tell
  426.      *        AmigaGuide to open the PriMan.guide file (which the user must have
  427.      *        placed either in the current directory or in the AmigaGuide search
  428.      *        path), note its signal bit (it uses one of our task's free signal
  429.      *        bits), and then return. PriMan's main loop listens for AmigaGuide
  430.      *        messages, and once it gets one that says AmigaGuide has finished
  431.      *        opening the file and is ready for further instructions, this
  432.      *        function is called again, and the next section of code is used
  433.      *        instead which actually opens the window...
  434.      */
  435.     if (!guideHandle)
  436.         {
  437.         /*
  438.          *        If PriMan's main window is not open (i.e. this is being called
  439.          *        as a result of a "?" argument from the Shell), then we won't
  440.          *        have a lock on any screen. In this case we check (but not lock)
  441.          *        whatever screen the user asked for in the settings, and open on
  442.          *        that. Otherwise we simply use the current screen. We always move
  443.          *        the chosen screen to the front in case it isn't already visible.
  444.          */
  445.         guideScreen = myScreen ? myScreen : LockOurScreen(FALSE);
  446.  
  447.         myGuide.nag_Screen = guideScreen;
  448.         myGuide.nag_Name = "PriMan.guide";
  449.  
  450.         if (guideHandle = OpenAmigaGuideAsync(&myGuide, NULL))
  451.             {
  452.             guideSignal = AmigaGuideSignal(guideHandle);
  453.             ScreenToFront(guideScreen);
  454.             }
  455.         }
  456.  
  457.     /*
  458.      *        The next alternative is that AmigaGuide is silently sitting in the
  459.      *        background with PriMan.guide loaded. Unless the main PriMan window
  460.      *        is not open (myScreen is null), we check if the AmigaGuide screen is
  461.      *        the same, and if it is, just ask AmigaGuide to display the first
  462.      *        node (Main). If the user has closed the AmigaGuide window himself,
  463.      *        or it hasn't been opened yet, this will have the effect of opening
  464.      *        the window. Otherwise, it will stay open and the first node will be
  465.      *        shown.
  466.      */
  467.     else if (!myScreen || guideScreen == myScreen)
  468.         SendAmigaGuideCmd(guideHandle, "LINK Main", NULL);
  469.  
  470.     /*
  471.      *        If we got here, AmigaGuide is running on the wrong screen. So we
  472.      *        close it down and call Help() again, taking us right back to square
  473.      *        one...
  474.      */
  475.     else
  476.         {
  477.         CloseAmigaGuide(guideHandle);
  478.         guideHandle = 0;
  479.         guideSignal = 0;
  480.         Help();
  481.         }
  482.     }
  483.  
  484.  
  485. /*
  486.  *        This closes down PriMan's interface, freeing up everything and
  487.  *        iconifying if necessary. If we're not running as a Commodity and can't
  488.  *        be iconified, it asserts the "all okay" error condition (which itself
  489.  *        will take care of freeing everything).
  490.  */
  491. void Hide()
  492.     {
  493.     if (!(iconify || commodity))
  494.         error = ALL_OKAY;
  495.     else
  496.         {
  497.         CloseSettingsWindow();
  498.         CloseMainWindow();
  499.         FreeRemember(&memoryKey, TRUE);
  500.         Iconify();
  501.         }
  502.     }
  503.  
  504.  
  505. /*
  506.  *        This tiny function adds PriMan's AppIcon to Workbench, if necessary.
  507.  *        For want of something better, we'll assign it an ID of 42...
  508.  */
  509. void Iconify()
  510.     {
  511.     if (iconify)
  512.         appIcon = AddAppIcon(42, NULL, "PriMan", appPort, NULL, wbIcon, TAG_END);
  513.     }
  514.  
  515.  
  516. /*
  517.  *        Get a lock on the screen we should be open on now. Opening on the
  518.  *        default public screen is easy (just pass a NULL to the lock function),
  519.  *        but opening on the frontmost screen is tricky (it might not even be a
  520.  *        public screen!). To achieve this, we walk through the public screen list
  521.  *        trying to find a match for what Intuition says its frontmost screen is.
  522.  *        If we can't find one, we open on the default public screen. If that
  523.  *        fails, we open on the Workbench screen. (And as Eddy says, if _that_
  524.  *        fails, then we're really screwed!)
  525.  *
  526.  *        If this function is only being used to check where we *should* be, pass
  527.  *        in FALSE to unlock the screen and just get back a pointer to what it was
  528.  *        (no guarantees that it won't have closed in the meantime!).
  529.  */
  530. struct Screen *LockOurScreen(BOOL keepLock)
  531.     {
  532.     char                    *screenName;    /* name of matching screen                    */
  533.  
  534.     struct List                *pubList;        /* Intuition's list of public screens        */
  535.     struct Screen            *screen;        /* frontmost screen    which we're looking for    */
  536.     struct PubScreenNode    *pubNode;        /* current screen being examined in list    */
  537.  
  538.     /*
  539.      *        We set screenName to NULL here, in the assumption that we'll be
  540.      *        opening on the default public screen. This only gets changed if we
  541.      *        want to open on the frontmost screen, and it is found in the public
  542.      *        screen list.
  543.      */
  544.     screenName = NULL;
  545.  
  546.     if (open == FRONTSCREEN)
  547.         {
  548.         pubList = LockPubScreenList();
  549.         screen = IntuitionBase -> FirstScreen;
  550.         FORLIST(pubList, pubNode)
  551.             if (pubNode -> psn_Screen == screen)
  552.                 {
  553.                 screenName = pubNode -> psn_Node.ln_Name;
  554.                 break;  /* found it, so drop out of loop */
  555.                 }
  556.         UnlockPubScreenList();
  557.         }
  558.  
  559.     /*
  560.      *        At this point, if screenName is NULL, then it is either because we
  561.      *        want to open on the default public screen, or because we didn't find
  562.      *        a match when walking through the public screen list. We try to lock
  563.      *        whatever it points to, and if that fails, lock the Workbench screen
  564.      *        as a last resort.
  565.      */
  566.     if (!(screen = LockPubScreen(screenName)))
  567.         screen = LockPubScreen("Workbench");
  568.  
  569.     /*
  570.      *        If the caller just wants to see what screen PriMan *should* be open
  571.      *        on, we unlock it here.
  572.      */
  573.     if (!keepLock)
  574.         UnlockPubScreen(NULL, screen);
  575.  
  576.     return screen;
  577.     }
  578.  
  579.  
  580. /*
  581.  *        Add "..." after the Kill and Signal menu items as necessary. If reset
  582.  *        is TRUE, remove the menu strip from the main window first. The menu
  583.  *        strip gets rejigged appropriately and added back into the window.
  584.  */
  585. void MenuEllipsis(struct Menu *menuStrip, BOOL reset)
  586.     {
  587.     static UBYTE *kill, *signal;  /* pointers to Kill and Signal menu item text */
  588.  
  589.     if (reset)
  590.         ClearMenuStrip(mainWindow);
  591.  
  592.     if (confirm)
  593.         {
  594.         kill = "Kill...";
  595.         signal = "Signal...";
  596.         }
  597.     else
  598.         {
  599.         kill = "Kill";
  600.         signal = "Signal";
  601.         }
  602.  
  603.     ((struct IntuiText *)ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_KILL, NOSUB)) -> ItemFill) -> IText = kill;
  604.     ((struct IntuiText *)ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_SIGNAL, NOSUB)) -> ItemFill) -> IText = signal;
  605.  
  606.     if (LayoutMenus(menuStrip, visInfo,
  607.                     GTMN_NewLookMenus, TRUE,
  608.                     TAG_END))
  609.         SetMenuStrip(mainWindow, menuStrip);
  610.     else
  611.         error = MENU_ERROR;
  612.     }
  613.  
  614.  
  615. /*
  616.  *        Put up a different gadget list in the Settings window. Uses the page
  617.  *        variable to see what is currently showing (or -1 if nothing). If this is
  618.  *        the same as what is being asked for, we don't need to do anything.
  619.  *        Otherwise, remove the old gadget list, replace it with the new one, and
  620.  *        update the page variable.
  621.  */
  622. void NewPage(WORD newPage)
  623.     {
  624.     if (page != newPage)
  625.         {
  626.         if (page != -1)
  627.             {
  628.             /*
  629.              *        If there was something showing already, we need to remove
  630.              *        those gadgets, and blank that portion of the window.
  631.              */
  632.             RemoveGList(setWindow, setGads[page], -1);
  633.             blank.Width = setWindow -> Width - INTERWIDTH * 4;
  634.             blank.Height = setGadHeight;
  635.             EraseImage(setWindow -> RPort, &blank, INTERWIDTH * 2, setGadStart);
  636.             }
  637.  
  638.         page = newPage;
  639.         AddGList(setWindow, setGads[page], ~0, -1, NULL);
  640.         RefreshGList(setGads[page], setWindow, NULL, -1);
  641.         GT_RefreshWindow(setWindow, NULL);
  642.         }
  643.     }
  644.  
  645.  
  646. /*
  647.  *        Restore the pointer to normal, enabling gadgets and window sizing. This
  648.  *        essentially reverses everything done in BusyPointer(). We don't do 
  649.  *        anything if there are more pending calls to NormalPointer().
  650.  */
  651. void NormalPointer()
  652.     {
  653.     if (--busyCount == 0)
  654.         {
  655.         if (mainWindow)
  656.             {
  657.             ClearPointer(mainWindow);
  658.             EndRequest(&req1, mainWindow);
  659.             WindowLimits(mainWindow, minWidth, minHeight, ~0, ~0);
  660.             }
  661.     
  662.         if (setWindow)
  663.             {
  664.             ClearPointer(setWindow);
  665.             EndRequest(&req2, setWindow);
  666.             }
  667.         }
  668.     }
  669.  
  670.  
  671. /*
  672.  *        This tiny function is used whenever a task gets deselected. It simply
  673.  *        disables the Kill, Priority, Signal, Frozen and Wide Slider menu items,
  674.  *        as well as invalidating pos.
  675.  */
  676. void OffTask()
  677.     {
  678.     OffMenu(mainWindow, FULLMENUNUM(M_TASK, I_KILL, NOSUB));
  679.     OffMenu(mainWindow, FULLMENUNUM(M_TASK, I_PRIORITY, NOSUB));
  680.     OffMenu(mainWindow, FULLMENUNUM(M_TASK, I_SIGNAL, NOSUB));
  681.     OffMenu(mainWindow, FULLMENUNUM(M_TASK, I_FROZEN, NOSUB));
  682.     OffMenu(mainWindow, FULLMENUNUM(M_TASK, I_WIDE, NOSUB));
  683.     pos = -1;
  684.     }
  685.  
  686.  
  687. /*
  688.  *        This function is used whenever a task gets selected. It updates the
  689.  *        slider gadget to reflect the task's priority, sets it to the correct
  690.  *        scale, and enables the Kill, Priority, Signal, Frozen and (if
  691.  *        appropriate) Wide Slider menu items.
  692.  */
  693. void OnTask()
  694.     {
  695.     struct MenuItem *item;  /* Wide Slider menu item */
  696.  
  697.     /*
  698.      *        First we want to temporarily remove the menu strip, and get the
  699.      *        address of the Wide Slider menu item. The menu strip gets put back
  700.      *        after updating the menu item.
  701.      */
  702.     ClearMenuStrip(mainWindow);
  703.     item = ItemAddress(menuStrip, FULLMENUNUM(M_TASK, I_WIDE, NOSUB));
  704.  
  705.     if (current -> ln_Pri > -26 && current -> ln_Pri < 26)
  706.         {
  707.         /*
  708.          *        We can use a narrow scale, so reset the menu's
  709.          *        checkmark, enable the menu item (allowing the user
  710.          *        to move to a wide scale), then update the slider
  711.          *        gadget's scale.
  712.          */
  713.         item -> Flags &= ~CHECKED;
  714.         item -> Flags |= ITEMENABLED;
  715.         WideSlider(FALSE);
  716.         }
  717.     else
  718.         {
  719.         /*
  720.          *        We're forced into using a wide scale. Set the menu's
  721.          *        checkmark but don't allow the user to change it!
  722.          */
  723.         item -> Flags |= CHECKED;
  724.         item -> Flags &= ~ITEMENABLED;
  725.         WideSlider(TRUE);
  726.         }
  727.  
  728.     ResetMenuStrip(mainWindow, menuStrip);
  729.  
  730.     /*
  731.      *        Now we change the value of the slider gadget.
  732.      */
  733.     GT_SetGadgetAttrs(sliderGad, mainWindow, NULL,
  734.                         GTSL_Level,        current -> ln_Pri,
  735.                         GA_Disabled,    FALSE,
  736.                         TAG_END);
  737.  
  738.     /*
  739.      *        And finally, we make sure the appropriate menu items are enabled.
  740.      */
  741.     OnMenu(mainWindow, FULLMENUNUM(M_TASK, I_KILL, NOSUB));
  742.     OnMenu(mainWindow, FULLMENUNUM(M_TASK, I_PRIORITY, NOSUB));
  743.     OnMenu(mainWindow, FULLMENUNUM(M_TASK, I_SIGNAL, NOSUB));
  744.     OnMenu(mainWindow, FULLMENUNUM(M_TASK, I_FROZEN, NOSUB));
  745.     }
  746.  
  747.  
  748. /*
  749.  *        Simulate a gadget being pressed (when the keyboard shortcut is used).
  750.  *        The gadget is marked as "selected", there is a delay, and then it is put
  751.  *        back to normal again.
  752.  */
  753. void PressGadget(struct Window *window, struct Gadget *gadget)
  754.     {
  755.     gadget -> Flags ^= GFLG_SELECTED;
  756.     RefreshGList(gadget, window, NULL, 1);
  757.     
  758.     Delay(PressDelay);
  759.     
  760.     gadget -> Flags ^= GFLG_SELECTED;
  761.     RefreshGList(gadget, window, NULL, 1);
  762.     }
  763.  
  764.  
  765. /*
  766.  *        Make sure a variable lies within the specified range, by increasing or
  767.  *        decreasing it if necessary. Used to keep the main window's size inside
  768.  *        acceptable limits.
  769.  */
  770. void Range(WORD *current, WORD min, WORD max)
  771.     {
  772.     if (*current < min)
  773.         *current = min;
  774.     else if (*current > max)
  775.         *current = max;
  776.     }
  777.  
  778.  
  779. /*
  780.  *        Request a font from the user. This function handles both the Gadget and
  781.  *        List font requesters, and initialises them beforehand if necessary. It
  782.  *        takes in the parameters below, and returns the result of AslRequest(),
  783.  *        after updating the Settings window's font box and related variables.
  784.  *
  785.  *            mainReq        pointer to a pointer of the requester to be used
  786.  *            otherReq    pointer to the other requester
  787.  *            initialName    name of the initially-selected font in the requester
  788.  *            initialSize pointer to the size of that font
  789.  *            fontGadget    the text box showing the selected font's name
  790.  *            fontString    the font name and size inside that text box
  791.  *            title        title of the requester's window
  792.  *            flags        flags to be passed (either zero or the fixed-width flag)
  793.  */
  794. BOOL RequestFont(struct FontRequester **mainReq, struct FontRequester *otherReq,
  795.                  char *initialName, WORD *initialSize,
  796.                  struct Gadget *fontGadget, char *fontString,
  797.                  char *title, ULONG flags)
  798.     {
  799.     ULONG    reqTag;        /* says whether or not we're using width tag    */
  800.     WORD    reqWidth,    /* initial width of requester                    */
  801.             reqHeight;    /* initial height of requester                    */
  802.     BOOL    result;        /* return value from AslRequest()                */
  803.  
  804.     BusyPointer();
  805.  
  806.     /*
  807.      *        If this is the first time the user has played with the requester, we
  808.      *        need to allocate memory for it, and initialise it. We do this now
  809.      *        (rather than when PriMan is first launched) so we can make a good
  810.      *        guess at what height is best. If the other requester has been opened
  811.      *        before, we use its height and width, otherwise we use 2/3 of the
  812.      *        screen's height and the default width.
  813.      */
  814.     if (!*mainReq)
  815.         {
  816.         if (otherReq)
  817.             {
  818.             reqHeight = otherReq -> fo_Height;
  819.             reqWidth = otherReq -> fo_Width;
  820.             reqTag = ASLFO_InitialWidth;
  821.             }
  822.         else
  823.             {
  824.             reqHeight = myScreen -> Height * 2 / 3;
  825.             reqWidth = 0;  /* not strictly needed since it's ignored */
  826.             reqTag = TAG_IGNORE;
  827.             }
  828.  
  829.         *mainReq = AllocAslRequestTags(ASL_FontRequest,
  830.                                         ASLFO_TitleText,        title,
  831.                                         ASLFO_InitialHeight,    reqHeight,
  832.                                         reqTag,                    reqWidth,
  833.                                         ASLFO_Flags,            flags,
  834.                                         TAG_END);
  835.         }
  836.  
  837.     /*
  838.      *        The requester gets offset from the Settings window in
  839.      *        same way as the Settings window gets offset from the
  840.      *        main window (by the dimensions of the close gadget).
  841.      */
  842.     if (result = AslRequestTags(*mainReq,
  843.                                 ASLFO_Screen,            myScreen,
  844.                                 ASLFO_TextAttr,            &propTA,  /* use gadget font for imagery */
  845.                                 ASLFO_InitialLeftEdge,    setWindow -> LeftEdge + CloseBoxWidth,
  846.                                 ASLFO_InitialTopEdge,    setWindow -> TopEdge + windowTop,
  847.                                 ASLFO_InitialName,        initialName,
  848.                                 ASLFO_InitialSize,        *initialSize,
  849.                                 TAG_END))
  850.         {
  851.         /*
  852.           *        Here we update the font text box in the Settings window, and
  853.          *        copy the font information from the requester. (This doesn't
  854.          *        happen if the user clicked Cancel.)
  855.          */
  856.         FontString(&((*mainReq) -> fo_Attr), fontString);
  857.         GT_SetGadgetAttrs(fontGadget, setWindow, NULL,
  858.                             GTTX_Text, fontString,
  859.                             TAG_END);
  860.         strcpy(initialName, (*mainReq) -> fo_Attr.ta_Name);
  861.         *initialSize = (*mainReq) -> fo_Attr.ta_YSize;
  862.         }
  863.  
  864.     NormalPointer();
  865.     return result;
  866.     }
  867.  
  868.  
  869. /*
  870.  *        Set up the Commodity interface. This involves creating a broker, and
  871.  *        attaching to it a filter, sender and translator.
  872.  */
  873. void SetupCommodity()
  874.     {
  875.     LONG brokerError;  /* error returned when creating a new broker */
  876.  
  877.     /*
  878.      *        This is the error requester in case something went wrong when setting up
  879.      *        the Commodity's hotkey.
  880.      */
  881.     struct EasyStruct badFilter =
  882.         {
  883.         sizeof(struct EasyStruct),
  884.         0,
  885.         "PriMan warning",
  886.         "Invalid hotkey - suggest you change it.",
  887.         "Okay"
  888.         };
  889.  
  890.     if (broker = CxBroker(&newBroker, &brokerError))
  891.         {
  892.         /*
  893.           *        Create the filter, which monitors input events for our hotkey.
  894.          */
  895.         if (filter = CxFilter(hotkey))
  896.             {
  897.             AttachCxObj(broker, filter);
  898.  
  899.             /*
  900.              *        Create the sender, which sends a message to our port once
  901.              *        the hotkey is spotted.
  902.              */
  903.             if (sender = CxSender(cxPort, 1))
  904.                 {
  905.                 AttachCxObj(filter, sender);
  906.  
  907.                 /*
  908.                  *        Create the translator. This one is a dummy translator,
  909.                  *        and simply removes the hotkey from the input stream, so
  910.                  *        no other application can use it.
  911.                  */
  912.                 if (translate = CxTranslate(NULL))
  913.                     {
  914.                     AttachCxObj(filter, translate);
  915.                     ActivateCxObj(broker, 1);
  916.                     if (CxObjError(filter) == COERR_BADFILTER)
  917.                         SimpleRequest(&badFilter, NULL);
  918.                     }
  919.                 else
  920.                     error = CX_ERROR;
  921.                 }
  922.             else
  923.                 error = CX_ERROR;
  924.             }
  925.         else
  926.             error = CX_ERROR;
  927.         }
  928.     else
  929.         /*
  930.          *        Here, there are two possibilities:
  931.          *
  932.          *        - A similar broker already exists. In this case, we silently
  933.          *          shut down (and let the copy of PriMan which must already be
  934.          *          running take control).
  935.           *
  936.          *        - There was an error creating the broker. We do the same as when
  937.          *          there was an error creating anything else above: raise the
  938.          *          appropriate error condition.
  939.          */
  940.         error = brokerError == CBERR_DUP ? ALL_OKAY : CX_ERROR;
  941.     }
  942.  
  943.  
  944. /*
  945.  *        Reveal PriMan's interface. There are several possibilities here:
  946.  *
  947.  *        - No windows are showing. In this case, we remove the AppIcon if
  948.  *          necessary, then simply open the main window.
  949.  *
  950.  *        - The main window is open, but it's not on the correct screen. So we
  951.  *          shut everything down and reopen, which will get us onto the correct
  952.  *          screen. It is important to remember if the Settings window is open,
  953.  *          so that can follow us around as well.
  954.  *
  955.  *        - The main window is both open, and on the correct screen. All we need
  956.  *          to do is move both the main window and its screen to the front, and
  957.  *          the Settings window too if that's also open.
  958.  */
  959. void Show()
  960.     {
  961.     BOOL            reopenSettings;        /* Settings window was open when we jumped screen    */
  962.     struct Screen    *tempScreen;        /* screen we should be on                            */
  963.  
  964.     if (!mainWindow)
  965.         {
  966.         if (appIcon)
  967.             {
  968.             RemoveAppIcon(appIcon);
  969.             appIcon = NULL;
  970.             }
  971.         OpenMainWindow();
  972.         }
  973.     else
  974.         {
  975.         /*
  976.          *        Check what screen we should be on, but don't actually lock it.
  977.          */
  978.         tempScreen = LockOurScreen(FALSE);
  979.         if (tempScreen != myScreen)
  980.             {
  981.             reopenSettings = (setWindow != NULL);
  982.             CloseSettingsWindow();
  983.             CloseMainWindow();
  984.             OpenMainWindow();
  985.             if (reopenSettings)
  986.                 OpenSettingsWindow();
  987.             }
  988.         else
  989.             {
  990.             WindowToFront(mainWindow);
  991.             if (setWindow)
  992.                 {
  993.                 WindowToFront(setWindow);
  994.                 ActivateWindow(setWindow);
  995.                 }
  996.             else
  997.                 ActivateWindow(mainWindow);
  998.             ScreenToFront(myScreen);
  999.             }
  1000.         }
  1001.     }
  1002.  
  1003.  
  1004. /*
  1005.  *        Put up a busy pointer and open the requester passed into this function.
  1006.  *        If the requester structure requires further arguments, they can be
  1007.  *        passed in as well.
  1008.  */
  1009. LONG SimpleRequest(struct EasyStruct *easyReq, APTR args)
  1010.     {
  1011.     LONG result;  /* return value from EasyRequest() */
  1012.  
  1013.     BusyPointer();
  1014.     result = EasyRequest(NULL, easyReq, NULL, args);
  1015.     NormalPointer();
  1016.     return result;
  1017.     }
  1018.  
  1019.  
  1020. /*
  1021.  *        This version of sprintf uses the exec routine, courtesy of Eddy Carroll
  1022.  *        and SnoopDos. Saves pulling in a lot of unnecessary library routines.
  1023.  *        The only difference between this and the one in stdio is that this
  1024.  *        always returns 0 instead of the length of the string.
  1025.  */
  1026. int __regargs sprintf(char *outstr, char *fmtstr, ...)
  1027.     {
  1028.     RawDoFmt(fmtstr, &fmtstr + 1, RAWDOFMT_COPY, outstr);
  1029.     return 0;
  1030.     }
  1031.  
  1032.  
  1033. /*
  1034.  *        Increment or decrement a value, making sure it stays within 0..range.
  1035.  *        This is useful when using the keyboard shortcuts for a cycle gadget.
  1036.  */
  1037. WORD Step(WORD value, WORD range, UWORD decrement)
  1038.     {
  1039.     if (decrement)
  1040.         {
  1041.         value--;
  1042.         if (value < 0)
  1043.             value = range;
  1044.         }
  1045.     else
  1046.         {
  1047.         value++;
  1048.         if (value > range)
  1049.             value = 0;
  1050.         }
  1051.     return value;
  1052.     }
  1053.  
  1054.  
  1055. /*
  1056.  *        Check to see if a task still exists. This works by checking the State
  1057.  *        field, and comparing against a list of valid states. Hence it is not
  1058.  *        100% accurate (the task might not exist, but that particular byte of
  1059.  *        memory happens to be set the way we want it), but it'll have to do...
  1060.  */
  1061. BOOL ValidTask(struct Task *task)
  1062.     {
  1063.     switch (task -> tc_State)
  1064.         {
  1065.         case TS_RUN:
  1066.         case TS_READY:
  1067.         case TS_WAIT:
  1068.         case FROZEN:
  1069.         case FROZENREADY:
  1070.             return TRUE;
  1071.             break;
  1072.  
  1073.         default:
  1074.             return FALSE;
  1075.             break;
  1076.         }
  1077.     }
  1078.  
  1079.  
  1080. /*
  1081.  *        Change the slider gadget's scale to wide or narrow.
  1082.  */
  1083. void WideSlider(BOOL wide)
  1084.     {
  1085.     if (wide)
  1086.         GT_SetGadgetAttrs(sliderGad, mainWindow, NULL,
  1087.                             GTSL_Min,    -128,
  1088.                             GTSL_Max,    127,
  1089.                             TAG_END);
  1090.     else
  1091.         GT_SetGadgetAttrs(sliderGad, mainWindow, NULL,
  1092.                             GTSL_Min,    -25,
  1093.                             GTSL_Max,    25,
  1094.                             TAG_END);
  1095.     }
  1096.  
  1097.  
  1098. /*
  1099.  *        Remove all remaining messages from a port, then delete it. Used when
  1100.  *        shutting down PriMan's message ports. We do this while multitasking is
  1101.  *        disabled, so no other messages can arrive in the meantime.
  1102.  */
  1103. void WipePort(struct MsgPort *port)
  1104.     {
  1105.     struct Message *message;  /* current message in port */
  1106.     
  1107.     Forbid();
  1108.     while (message = GetMsg(port))
  1109.         ReplyMsg(message);
  1110.     DeleteMsgPort(port);
  1111.     Permit();
  1112.     }
  1113.